import {
  CRC16,
  stringToAsciiBytes,
  intToBytes,
  bytes2HexStr,
  MsgType,
  DeviceMessageSend,
  DeviceMessageReceive,
  CommonConfirmReplyMsgBody,
  RecipeSendMsgBody,
  RecipeStepInfo,
  RecipeStartCookingMsgBody,
  ElectronicScaleFoodPreparationMsgBody,
  FoodWeightInfo,
  RangeHoodFanControlMsgBody,
  SettingSeasoningRatioCalibrationMsgBody,
  SeasoningRatioCalibrationInfo,
  ManualAddSeasoningMsgBody,
  SeasoningAddInfo,
  SettingSeasoningBaseUnitMsgBody,
  simpleUUID,
  bytesToInt,
  hexStr2Bytes
} from '@/utils/deviceProtocolUtil.js'
import emitter from '@/utils/emitter'

import {
  DeviceModel,
  DeviceStatus,
  DeviceConnectStatus
} from '@/interface/device.js'
import { CookItemStatus } from '@/interface/cooking.js'

import { DeviceProtocolBase } from './DeviceProtocolBase'
import { useCookingStore } from '@/store/modules/cooking'

import { lingxinApiService } from '@/service/lingxinApiService.js'

// 设备事件类型
export const DeviceCallbackEventType = {
  // 设备链接状态变更事件
  DeviceConnectStatusChangeEvent: 'device-connect-status-change-event',
  // 调料投放回调结果
  ManualAddSeasoningResult: 'device-manual-add-seasoning-result'
}

/**
 * 监听消息设备上报服务
 */
class DeviceProtocolListenerService extends DeviceProtocolBase {
  constructor() {
    super()

    // 外部回调
    this.deviceMessageCallbackMap = new Map()

    // 注册发现服务的链接请求回调
    this.deviceDiscoverStore.registerDeviceBoardcastCallback(
      this.handleDeviceDiscoverRequest,
      this
    )

    // 注册设备TCP链接请求回调
    this.deviceProtocolStore.registerDeviceConnectRequestCallback(
      this.handleDeviceConnectRequest,
      this
    )

    // 注册发设备链接状态变更回调
    this.deviceProtocolStore.registerDeviceConnectStatusChangedCallback(
      this.handleDeviceConnectStatusChanged,
      this
    )
  }

  /**
   * 处理设备UDP广播的报文
   * @param {Object} deviceSession
   */
  handleDeviceDiscoverRequest(deviceSession) {
    g_utils.writeLog('开始处理UDP设备的广播请求：', deviceSession)
    let { sn, category } = deviceSession
    let deviceInfo = this.deviceStore.getDeviceInfoFromBindList(category, sn)
    g_utils.writeLog('查询绑定的设备信息：', deviceInfo)
    return deviceInfo != null
  }

  /**
   * 处理设备TCP链接请求
   * @param {Object} deviceSession
   */
  handleDeviceConnectRequest(deviceSession) {
    g_utils.writeLog('开始处理设备TCP链接请求：', deviceSession)
    let { sn, sessionId } = deviceSession
    let deviceInfo = this.deviceStore.getDeviceInfoFromBindListBySn(sn)
    g_utils.writeLog('查询绑定的设备信息：', deviceInfo)
    return deviceInfo != null
  }

  /**
   * 处理设备链接状态变更回调
   */
  handleDeviceConnectStatusChanged(data) {
    let { status, deviceSn, deviceAddress } = data
    g_utils.writeLog(`开始处理设备链接 ${status} 状态变更回调: ${deviceSn}`)

    // 发送设备链接状态事件
    let cbMsg = {
      sn: deviceSn,
      type: status,
      address: deviceAddress
    }
    emitter.emit(DeviceCallbackEventType.DeviceConnectStatusChangeEvent, cbMsg)

    // 找到对应的设备信息
    const { allDeviceList, bindDeviceList } = storeToRefs(useDeviceStore())
    let device = bindDeviceList.value.find(item => item.sn == deviceSn)
    if (!device) {
      g_utils.writeLog(`没有找到绑定设备: ${deviceSn}`)
      return
    }
    g_utils.writeLog('设备信息: ', device)

    if (status == 'connected') {
      // 在线
      let onlineDeviceList = this.getOnlineDeviceList()
      if (onlineDeviceList == null) {
        return
      }
      const matchingDeviceSession = onlineDeviceList.find(
        item2 => item2.sn === device.sn
      )
      g_utils.writeLog('设备会话: ', matchingDeviceSession)
      if (matchingDeviceSession) {
        device.ip = matchingDeviceSession.ip
      }
      device.onlineStatus = DeviceConnectStatus.Online // 设备在线
      device.status = DeviceStatus.Initializating // 默认设备为初始化中
    } else {
      // 离线
      device.onlineStatus = DeviceConnectStatus.Offline
    }
  }

  /**
   * 注册设备消息监听回调
   */
  registerDeviceMessageListener() {
    // 基础信息 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.DeviceBasicInfoReport,
        this.deviceBasicInfoReportListener,
        this
      )
    )
    // 自检上报 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.StatusSelfCheckReport,
        this.deviceStatusSelfCheckReportListener,
        this
      )
    )
    // 监测上报 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.PeripheralStatusReport,
        this.peripheralStatusReportReportListener,
        this
      )
    )
    // 工作状态上报 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.DeviceStatusReport,
        this.deviceWorkStatusReportListener,
        this
      )
    )

    // 炒菜进展 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.RecipeCookingStatus,
        this.cookingStatusListener,
        this
      )
    )
    // 完成炒菜 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.RecipeFinishCooking,
        this.finishCookingListener,
        this
      )
    )
    // 完成自动洗锅 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.RecipeFinishAutoWashPot,
        this.finishAutoWashPotListener,
        this
      )
    )
    // 完成手动洗锅 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.ManualFinishWashPot,
        this.finishManualWashPotListener,
        this
      )
    )
    // 调料投放结果 监听器
    this.deviceMessageListener.push(
      this.deviceProtocolStore.registerDeviceMessageCallback(
        null,
        MsgType.ManualAddSeasoningResult,
        this.manualAddSeasoningResultListener,
        this
      )
    )
  }
  /**
   * 卸载设备消息监听回调
   */
  unregisterDeviceMessageListener() {
    // 注销所有监听器
    for (let key of this.deviceMessageListener) {
      this.deviceProtocolStore.unregisterDeviceMessageCallback(key)
    }
  }

  /**
   * 基础信息-监听器
   * @param {DeviceMessageReceive} msg
   */
  deviceBasicInfoReportListener(msg) {
    g_utils.writeLog('接收到设备上报【基础信息】命令：', msg)
    // DeviceBasicInfoReportMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody
    g_utils.writeLog('准备更新设备信息：', msg.msgBody)

    const { allDeviceList, bindDeviceList } = storeToRefs(useDeviceStore())
    let targetDevice = bindDeviceList.value.find(
      element => element.sn == deviceSn
    )
    if (!targetDevice) {
      g_utils.writeLog('没有找到绑定的设备：', deviceSn)
      return
    }

    // 更新设备信息
    targetDevice.limitCount = msgBody.limit
    targetDevice.masterVersion = msgBody.mfir1mver
    targetDevice.firmware1Version = msgBody.mfir2mver

    g_utils.writeLog('更新后的设备信息：', targetDevice)
  }

  /**
   * 自检上报-监听器
   * @param {DeviceMessageReceive} msg
   */
  deviceStatusSelfCheckReportListener(msg) {
    g_utils.writeLog('接收到设备上报【自检上报】命令：', msg)
    // StatusSelfCheckReportMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody
    g_utils.writeLog('准备更新设备外设信息：', msg.msgBody)

    const { allDeviceList, bindDeviceList } = storeToRefs(useDeviceStore())
    let targetDevice = bindDeviceList.value.find(
      element => element.sn == deviceSn
    )
    if (!targetDevice) {
      g_utils.writeLog('没有找到绑定的设备：', deviceSn)
      return
    }

    // 更新设备的外设信息
    if (!targetDevice.peripheralList) {
      g_utils.writeLog('绑定的设备没有外设信息：', deviceSn)
      return
    }
    if (!msgBody.itemList) {
      g_utils.writeLog('设备没有上报外设信息：', deviceSn)
      return
    }
    for (let element of targetDevice.peripheralList) {
      let targetReportItem = msgBody.itemList.find(reportItem => {
        let driverIdStr = bytes2HexStr(intToBytes(reportItem.targetId, 2))
        return element.driverId == driverIdStr
      })
      if (targetReportItem) {
        // 更新外设地址
        element.driverId = bytes2HexStr(
          intToBytes(targetReportItem.targetId, 2)
        )
        // 更新外设通信状态
        element.control = targetReportItem.result
      }
    }
    g_utils.writeLog('更新后的设备信息：', targetDevice)
  }

  /**
   * 外设监测上报-监听器
   * @param {DeviceMessageReceive} msg
   */
  peripheralStatusReportReportListener(msg) {
    g_utils.writeLog('接收到设备上报【监测上报】命令：', msg)
    // StatusMonitorReportMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody
    g_utils.writeLog('准备更新设备外设信息：', msg.msgBody)

    const { allDeviceList, bindDeviceList } = storeToRefs(useDeviceStore())
    let targetDevice = bindDeviceList.value.find(
      element => element.sn == deviceSn
    )
    if (!targetDevice) {
      g_utils.writeLog('没有找到绑定的设备：', deviceSn)
      return
    }

    // 更新设备的外设信息
    if (!targetDevice.peripheralList) {
      g_utils.writeLog('绑定的设备没有外设信息：', deviceSn)
      return
    }
    if (!msgBody.itemList) {
      g_utils.writeLog('设备没有上报外设信息：', deviceSn)
      return
    }
    for (let element of targetDevice.peripheralList) {
      let targetReportItem = msgBody.itemList.find(reportItem => {
        let driverIdStr = bytes2HexStr(intToBytes(reportItem.targetId, 2))
        return element.driverId == driverIdStr
      })
      if (targetReportItem) {
        // TODO: 更新外设信息
      }
    }
    g_utils.writeLog('更新后的设备信息：', targetDevice)
  }

  /**
   * 设备工作状态上报-监听器
   * @param {DeviceMessageReceive} msg
   */
  deviceWorkStatusReportListener(msg) {
    g_utils.writeLog('接收到设备上报【工作状态上报】命令：', msg)
    // DeviceWorkStatusReportMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody

    const { allDeviceList, bindDeviceList } = storeToRefs(useDeviceStore())
    let targetDevice = bindDeviceList.value.find(
      element => element.sn == deviceSn
    )
    if (!targetDevice) {
      g_utils.writeLog('没有找到绑定的设备：', deviceSn)
      return
    }

    // 更新工作状态
    targetDevice.status = msgBody.work
    g_utils.writeLog('更新后的设备信息：', targetDevice)

    // 烹饪记录信息刷新
    let cookingStore = useCookingStore()
    let targetCookItem = cookingStore.getLatestCookInfoForDevice(deviceSn)
    if (targetCookItem) {
      // 根据设备上报的状态，刷新烹饪记录的状态
      switch (targetDevice.status) {
        case DeviceStatus.Cooking:
          if (
            targetCookItem.status == CookItemStatus.NotStart ||
            targetCookItem.status == CookItemStatus.Pause
          ) {
            targetCookItem.status = CookItemStatus.Cooking
          }
          break
      }
    }
  }

  /**
   * 炒菜进展-监听器
   * @param {DeviceMessageReceive} msg
   */
  cookingStatusListener(msg) {
    g_utils.writeLog('接收到设备上报【炒菜进展】命令：', msg)
    // RecipeCookingStatusMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody
    // 更新设备的烹饪进度状态
    g_utils.writeLog('准备更新设备烹饪状态信息：', msgBody)
    let deviceStatus = msgBody.work
    let step = msgBody.step
    let result = msgBody.result
    let status = msgBody.status

    let deviceStore = useDeviceStore()
    let targetDevice = deviceStore.getDeviceInfoFromBindListBySn(deviceSn)
    let cookingStore = useCookingStore()
    let targetCookItem = cookingStore.getLatestCookInfoForDevice(deviceSn)

    // 0，步骤执行错误
    // 1，步骤执行正确
    if (result == 1) {
      // 设备工作状态
      if (!targetDevice) {
        g_utils.writeLog('没有找到设备：', deviceSn)
        return
      }
      g_utils.writeLog('设备信息：', targetDevice)
      targetDevice.status = deviceStatus

      // 烹饪状态
      g_utils.writeLog('准备更新烹饪状态信息：', msgBody)
      if (!targetCookItem) {
        g_utils.writeLog('没有找到设备当前烹饪信息：', deviceSn)
        return
      }
      g_utils.writeLog('烹饪信息：', targetCookItem)
      targetCookItem.step = step
      targetCookItem.stepPauseDuration = 0
      targetCookItem.stepStartTime = Date.now()
      targetCookItem.status = CookItemStatus.Cooking
      if (targetCookItem.step == 1) {
        // 烹饪开始
        targetCookItem.cookStartTime = Date.now()
        targetCookItem.stepFaildReason = []
      }
    } else {
      // 烹饪步骤执行错误
      // TODO： 根据status的状态，反馈错误给到用户
      if (!targetCookItem) {
        g_utils.writeLog('没有找到设备当前烹饪信息：', deviceSn)
        return
      }
      if (targetCookItem.step == 1) {
        // 烹饪开始
        targetCookItem.cookStartTime = Date.now()
        targetCookItem.stepFaildReason = []
      }
      // 记录错误信息
      let stepFailedItem = targetCookItem.stepFaildReason.find(
        element => element.step == step
      )
      let reason = ''
      switch (status) {
        case 1:
          reason = '外设地址匹配错误'
          break
        case 2:
          reason = '命令发送失败'
          break
        case 3:
          reason = '命令执行错误'
          break
        default:
          reason = ''
          break
      }
      if (stepFailedItem) {
        stepFailedItem.time = Date.now()
        stepFailedItem.reason = reason // TODO: 需要按照status含义转成具体原因
      } else {
        targetCookItem.stepFaildReason.push({
          time: Date.now(), // 时间戳(毫秒)
          step: step, // 步骤，即第几步，从1开始
          reason: reason // 原因，中文，可以供人友好阅读
        })
      }
    }
    g_utils.writeLog('更新后的设备信息：', targetDevice)
    g_utils.writeLog('更新后的烹饪信息：', targetCookItem)
  }

  /**
   * 完成炒菜-监听器
   * @param {DeviceMessageReceive} msg
   */
  finishCookingListener(msg) {
    g_utils.writeLog('接收到设备上报【完成炒菜】命令：', msg)
    // DeviceWorkStatusMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody
    // 更新设备的烹饪进度状态
    g_utils.writeLog('准备更新设备烹饪状态信息：', msgBody)
    let deviceStore = useDeviceStore()
    let targetDevice = deviceStore.getDeviceInfoFromBindListBySn(deviceSn)
    if (!targetDevice) {
      g_utils.writeLog('没有找到设备：', deviceSn)
      return
    }
    g_utils.writeLog('设备信息：', targetDevice)
    // 设备工作状态
    targetDevice.status = DeviceStatus.WaitPlating

    g_utils.writeLog('准备更新烹饪状态信息：', msgBody)
    let cookingStore = useCookingStore()
    let targetCookItem = cookingStore.getLatestCookInfoForDevice(deviceSn)
    if (!targetCookItem) {
      g_utils.writeLog('没有找到设备当前烹饪信息：', deviceSn)
      return
    }
    g_utils.writeLog('烹饪信息：', targetCookItem)
    targetCookItem.status = CookItemStatus.Finish
    targetCookItem.cookEndTime = Date.now() // 烹饪结束

    // 音频参数
    const innerAudioContext = uni.createInnerAudioContext()
    innerAudioContext.autoplay = true
    innerAudioContext.volume = 1
    lingxinApiService
      .textToVoice(`${targetCookItem.recipeName}烹饪完成`)
      .then(filePath => {
        console.log('文本转语音成功-textToVoice: ', filePath)
        innerAudioContext.src = filePath
        innerAudioContext.play()
      })
      .catch(e => {
        console.log('文本转语音失败-textToVoice: ', e)
      })
  }

  /**
   * 完成自动洗锅-监听器
   * @param {DeviceMessageReceive} msg
   */
  finishAutoWashPotListener(msg) {
    g_utils.writeLog('接收到设备上报【完成自动洗锅】命令：', msg)
    // DeviceWorkStatusMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody
    // TODO：更新设备的烹饪进度状态
    g_utils.writeLog('准备更新设备状态信息：', msg.msgBody)
    let deviceStore = useDeviceStore()
    let targetDevice = deviceStore.getDeviceInfoFromBindListBySn(deviceSn)
    if (!targetDevice) {
      g_utils.writeLog('没有找到设备：', deviceSn)
      return
    }
    g_utils.writeLog('设备信息：', targetDevice)
    // 设备工作状态
    targetDevice.status = DeviceStatus.Idle
  }

  /**
   * 完成手动洗锅-监听器
   * @param {DeviceMessageReceive} msg
   */
  finishManualWashPotListener(msg) {
    g_utils.writeLog('接收到设备上报【完成手动洗锅】命令：', msg)
    // DeviceWorkStatusMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody
    // TODO：更新设备的烹饪进度状态
    g_utils.writeLog('准备更新设备状态信息：', msg.msgBody)
    let deviceStore = useDeviceStore()
    let targetDevice = deviceStore.getDeviceInfoFromBindListBySn(deviceSn)
    if (!targetDevice) {
      g_utils.writeLog('没有找到设备：', deviceSn)
      return
    }
    g_utils.writeLog('设备信息：', targetDevice)
    // 设备工作状态
    targetDevice.status = DeviceStatus.Idle
  }

  /**
   * 调料投放结果-监听器
   * @param {DeviceMessageReceive} msg
   */
  manualAddSeasoningResultListener(msg) {
    g_utils.writeLog('接收到设备上报【外设操作结果】命令：', msg)
    // DeviceWorkStatusMsgBody 类型
    let deviceSn = msg.sn
    let msgBody = msg.msgBody
    if (msgBody == null) {
      // 无需处理
      return
    }
    g_utils.writeLog('准备调用回调处理信息：', msgBody)
    let item = msgBody.itemList[0]
    let cbMsg = {
      deviceSn: deviceSn,
      driverId: bytes2HexStr(intToBytes(item.targetId, 2)),
      result: item.result,
      status: item.status
    }
    // 发送事件
    emitter.emit(DeviceCallbackEventType.ManualAddSeasoningResult, cbMsg)
  }
}

export const deviceProtocolListenerService = new DeviceProtocolListenerService()
